﻿#region File Info Header
/*________________________________________________________________________________________

  Copyright (C) 2011 Jason Zhang, eagleboost@msn.com

  * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, 
  * EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED 
  * WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.

________________________________________________________________________________________*/
#endregion File Info Header

namespace EagleBoost.Wpf.Presentation.Controls.ColorPicker
{
  using System;
  using System.Linq;
  using System.Reflection;
  using System.Windows;
  using System.Windows.Controls;
  using System.Windows.Media;
  using System.Collections.Generic;
  using Extensions;

  /// <summary>
  /// ColorPickerControl
  /// </summary>
  [TemplatePart(Name = PartNoColorGrid, Type = typeof(AutoLayoutGrid))]
  [TemplatePart(Name = PartThemeBaseColorsGrid, Type = typeof(AutoLayoutGrid))]
  [TemplatePart(Name = PartThemeColorsGrid, Type = typeof(AutoLayoutGrid))]
  [TemplatePart(Name = PartBasicColorsGrid, Type = typeof(AutoLayoutGrid))]
  [TemplatePart(Name = PartMoreColorsButton, Type = typeof(AutoLayoutGrid))]
  public class ColorPickerControl : UserControl
  {
    #region Constants
    private const string PartNoColorGrid = "PART_NoColorGrid";
    private const string PartThemeBaseColorsGrid = "PART_ThemeBaseColorsGrid";
    private const string PartThemeColorsGrid = "PART_ThemeColorsGrid";
    private const string PartBasicColorsGrid = "PART_BasicColorsGrid";
    private const string PartMoreColorsButton = "PART_MoreColorsButton";

    private const string ColorGroup = "ColorGroup";
    private const string ControlXamlFile = "component/Controls/ColorPicker/ColorPickerControl.xaml";
    #endregion Constants

    #region Declarations
    private static readonly Style ControlStyle;
    private static readonly Style ColorButtonStyle;
    private IEnumerable<Color> _availableColors;
    private IEnumerable<Color> _availableThemeBaseColors;
    private IEnumerable<Color> _availableThemeColors;
    private IEnumerable<Color> _availableBasicColors;
    #endregion Declarations

    #region ctors
    static ColorPickerControl()
    {
      var assembly = System.IO.Path.GetFileNameWithoutExtension(Assembly.GetExecutingAssembly().ManifestModule.Name);
      var rd = Application.LoadComponent(new Uri(string.Format("{0};{1}", assembly, ControlXamlFile), UriKind.Relative)) as ResourceDictionary;
      if (rd != null)
      {
        ControlStyle = rd["ColorPickerControl"] as Style;
        ColorButtonStyle = rd["ColorButton"] as Style;
      }

      StyleProperty.OverrideMetadata(typeof(ColorPickerControl), new FrameworkPropertyMetadata(ControlStyle));
      DefaultStyleKeyProperty.OverrideMetadata(typeof(ColorPickerControl), new FrameworkPropertyMetadata(typeof(ColorPickerControl)));
    }
    #endregion ctors

    #region Dependency Properties
    public static readonly DependencyProperty SelectedColorProperty =
      DependencyProperty.Register("SelectedColor", typeof(Color?), typeof(ColorPickerControl),
      new PropertyMetadata(null, SelectedColorChanged, CoerceSelectedColorCallback));

    /// <summary>
    /// Selected Color
    /// </summary>
    public Color? SelectedColor
    {
      get { return (Color?)GetValue(SelectedColorProperty); }
      set { SetValue(SelectedColorProperty, value); }
    }

    public static readonly DependencyProperty SelectedBrushProperty =
      DependencyProperty.Register("SelectedBrush", typeof(Brush), typeof(ColorPickerControl),
      new PropertyMetadata(null, SelectedBrushChanged));

    /// <summary>
    /// Selected Brush
    /// </summary>
    public Brush SelectedBrush
    {
      get { return (Brush)GetValue(SelectedBrushProperty); }
      set { SetValue(SelectedBrushProperty, value); }
    }

    public static readonly DependencyProperty ShowColorToolTipProperty =
      DependencyProperty.Register("ShowColorToolTip", typeof(bool), typeof(ColorPickerControl));

    /// <summary>
    /// Show color name as tool tip
    /// </summary>
    public bool ShowColorToolTip
    {
      get { return (bool)GetValue(ShowColorToolTipProperty); }
      set { SetValue(ShowColorToolTipProperty, value); }
    }

    internal static readonly DependencyProperty IsSelectedColorChangedProperty =
      DependencyProperty.Register("IsSelectedColorChanged", typeof(bool), typeof(ColorPickerControl));

    /// <summary>
    /// Internal use, indicating the selected color is changed
    /// </summary>
    internal bool IsSelectedColorChanged
    {
      get { return (bool)GetValue(IsSelectedColorChangedProperty); }
      set { SetValue(IsSelectedColorChangedProperty, value); }
    }

    public static readonly DependencyProperty IsAdvancedOptionVisibleProperty =
      DependencyProperty.Register("IsAdvancedOptionVisible", typeof(bool), typeof(ColorPickerControl),
      new PropertyMetadata(false));

    /// <summary>
    /// Show acvanced options
    /// </summary>
    public bool IsAdvancedOptionVisible
    {
      get { return (bool)GetValue(IsAdvancedOptionVisibleProperty); }
      set { SetValue(IsAdvancedOptionVisibleProperty, value); }
    }
    #endregion Dependency Properties

    #region Private Properties
    private AutoLayoutGrid NoColorGrid
    {
      get { return Template.FindName(PartNoColorGrid, this) as AutoLayoutGrid; }
    }

    private AutoLayoutGrid ThemeBaseColorsGrid
    {
      get { return Template.FindName(PartThemeBaseColorsGrid, this) as AutoLayoutGrid; }
    }

    private AutoLayoutGrid ThemeColorsGrid
    {
      get { return Template.FindName(PartThemeColorsGrid, this) as AutoLayoutGrid; }
    }

    private AutoLayoutGrid BasicColorsGrid
    {
      get { return Template.FindName(PartBasicColorsGrid, this) as AutoLayoutGrid; }
    }

    private Button MoreColorButton
    {
      get { return Template.FindName(PartMoreColorsButton, this) as Button; }
    }
    #endregion Private Properties

    #region Overrides
    public override void OnApplyTemplate()
    {
      base.OnApplyTemplate();

      if (NoColorGrid == null || ThemeBaseColorsGrid == null || ThemeColorsGrid == null 
        || BasicColorsGrid == null || MoreColorButton == null)
      {
        return;
      }

      //No Color Button
      NoColorGrid.ColumnDefinitions.Add(new ColumnDefinition());
      var noColorButton = new RadioButton
                            {
                              Background = new SolidColorBrush(StandardColors.TransparentColor),
                              GroupName = ColorGroup,
                              Content = "No Color",
                              Style = ColorButtonStyle,
                              IsChecked = SelectedColor == StandardColors.TransparentColor,
                              ToolTip = ShowColorToolTip ? StandardColors.TransparentColor.ColorString() : null
                            };
      noColorButton.Click += ChooseColor;
      NoColorGrid.Children.Add(noColorButton);

      //Theme Base Color buttons
      for (var i = 0; i < 10; i++)
      {
        ThemeBaseColorsGrid.ColumnDefinitions.Add(new ColumnDefinition());
      }

      foreach (var color in AvailableThemeBaseColors)
      {
        var button = new RadioButton
                       {
                         Background = new SolidColorBrush(color), 
                         GroupName = ColorGroup, 
                         Style = ColorButtonStyle,
                         ToolTip = ShowColorToolTip ? color.ColorString() : null
                       };
        button.Click += ChooseColor;
        ThemeBaseColorsGrid.Children.Add(button);
        if (color == SelectedColor)
        {
          button.IsChecked = true;
        }
      }

      //Theme Color buttons
      for (int i = 0; i < 5; i++)
      {
        ThemeColorsGrid.RowDefinitions.Add(new RowDefinition());
      }

      foreach (var color in AvailableThemeColors)
      {
        var button = new RadioButton
                       {
                         Background = new SolidColorBrush(color),
                         GroupName = ColorGroup,
                         Style = ColorButtonStyle,
                         ToolTip = ShowColorToolTip ? color.ColorString() : null
                       };
        button.Click += ChooseColor;
        ThemeColorsGrid.Children.Add(button);
        if (color == SelectedColor)
        {
          button.IsChecked = true;
        }
      }

      //Basic Color buttons
      for (int i = 0; i < 10; i++)
      {
        BasicColorsGrid.ColumnDefinitions.Add(new ColumnDefinition());
      }

      foreach (var color in AvailableBasicColors)
      {
        var button = new RadioButton
                       {
                         Background = new SolidColorBrush(color),
                         GroupName = ColorGroup,
                         Style = ColorButtonStyle,
                         ToolTip = ShowColorToolTip ? color.ColorString() : null
                       };
        button.Click += ChooseColor;
        BasicColorsGrid.Children.Add(button);
        if (color == SelectedColor)
        {
          button.IsChecked = true;
        }
      }

      MoreColorButton.Click += (s, e) => { };
    }
    #endregion Overrides

    #region Public Properties
    public IEnumerable<Color> AvailableColors
    {
      get { return _availableColors ?? StandardColors.AvailableColors; }
      set { _availableColors = value; }
    }

    public IEnumerable<Color> AvailableThemeBaseColors
    {
      get { return _availableThemeBaseColors ?? StandardColors.AvailableThemeBaseColors; }
      set { _availableThemeBaseColors = value; }
    }

    public IEnumerable<Color> AvailableThemeColors
    {
      get { return _availableThemeColors ?? StandardColors.AvailableThemeColors; }
      set { _availableThemeColors = value; }
    }

    public IEnumerable<Color> AvailableBasicColors
    {
      get { return _availableBasicColors ?? StandardColors.AvailableBasicColors; }
      set { _availableBasicColors = value; }
    }	  
    #endregion Public Properties

    #region Private Methods
    private void UpdateSelectedButton()
    {
      foreach (var button in this.FindChildren<RadioButton>())
      {
        button.IsChecked = false;
        if (((SolidColorBrush)button.Background).Color == SelectedColor)
        {
          button.IsChecked = true;
        }
      }
    }
    #endregion Private Methods

    #region Event Handlers
    private static void SelectedColorChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
    {
      var control = (ColorPickerControl)obj;
      control.UpdateSelectedButton();
      var color = args.NewValue as Color?;
      if (color.HasValue)
      {
        control.SelectedBrush = new SolidColorBrush(color.Value);
      }
    }

    private static object CoerceSelectedColorCallback(DependencyObject obj, object baseValue)
    {
      var control = (ColorPickerControl)obj;
      if ((baseValue is Color) && control.AvailableColors.Contains((Color)baseValue))
      {
        return baseValue;
      }

      return control.SelectedColor;
    }

    private static void SelectedBrushChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
    {
      var control = (ColorPickerControl) obj;
      var solidColorBrush = args.NewValue as SolidColorBrush;
      control.SelectedColor = solidColorBrush != null ? (Color?) solidColorBrush.Color : null;
    }

    private void ChooseColor(object sender, RoutedEventArgs e)
    {
      SelectedColor = ((SolidColorBrush) ((RadioButton) sender).Background).Color;
      IsSelectedColorChanged = true;
    }
    #endregion Event Handlers
  }
}
